package com.glsc.ngateway.oaflow.service;

import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONObject;
import com.glsc.ngateway.common.api.oaflow.dto.SyncPlatformFlowCommonDto;
import com.glsc.ngateway.common.api.oaflow.dto.other.FlowObject;
import com.glsc.ngateway.common.base.domain.mysql.gateway.oaflow.TOaFlowMetaTable;
import com.glsc.ngateway.common.base.domain.mysql.gateway.oaflow.TOaFlowSyncLog;
import com.glsc.ngateway.common.base.domain.oracle.ecology.WorkflowRequestbase;
import com.glsc.ngateway.common.base.enums.OaFlowSyncLogStatusEnum;
import com.glsc.ngateway.common.base.enums.OaFlowSyncMessageDescriptionEnum;
import com.glsc.ngateway.common.base.enums.OaWorkFlowRequestTableStatusEnum;
import com.glsc.ngateway.common.base.repo.mysql.gateway.oaflow.TOaFlowSyncLogRepository;
import com.glsc.ngateway.common.base.repo.oracle.ecology.WorkflowRequestbaseRepository;
import com.glsc.ngateway.common.base.utils.redis.IbltRedis;
import com.glsc.ngateway.common.base.utils.redis.RedisKey;
import com.glsc.ngateway.oaflow.controller.OaCallController;
import com.glsc.ngateway.oaflow.utils.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 同步oa和自研网关
 *
 * @author xuchang
 */

@Service
public class OaSyncService {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    FlowService flowService;
    @Autowired
    private MetaFlowService metaFlowService;
    @Autowired
    private AddFlowService addFlowService;
    @Autowired
    private TOaFlowSyncLogRepository syncLogRepo;
    @Autowired
    private WorkflowRequestbaseRepository oaFlowRepo;
    @Resource
    private OaCallController oaCallController;
    @Resource
    private IbltRedis ibltRedis;

    /**
     * 向oa同步流程
     * TODO:改为统计list，批量操作
     */
    public void syncFlow2Oa(SyncPlatformFlowCommonDto<?> param) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        if(CollectionUtils.isEmpty(param.getDataList())){
            logger.info("没有流程需要想oa同步");
            return;
        }
        List<String> taskIds = param.getDataList().stream().map(SyncPlatformFlowCommonDto.GatewayFlowDataDto::getTaskId).collect(Collectors.toList());
        //只处理未同步和已同步的记录 Map<taskId, Map<workflowId, TOaFlowSyncLog>>
        Map<String, Map<String, TOaFlowSyncLog>> logMap = syncLogRepo.findAllByTaskIdIn(taskIds)
                .stream()
                .collect(Collectors.groupingBy(TOaFlowSyncLog::getTaskId, Collectors.toMap(TOaFlowSyncLog::getWorkflowId, o -> o)));
        for (SyncPlatformFlowCommonDto.GatewayFlowDataDto<?> d : param.getDataList()) {
            //platform推送的待办item
            //taskId, workflowId 做联合筛选是否有记录
            if (logMap.containsKey(d.getTaskId()) && logMap.get(d.getTaskId()).containsKey(d.getWorkflowId())) {
                //处理过的移除掉
                TOaFlowSyncLog log = logMap.get(d.getTaskId()).remove(d.getWorkflowId());
                //不再同步，继续循环
                if (!OaFlowSyncLogStatusEnum.canSync(log.getStatus())) {
                    //其他状态不做处理
                    logger.info(String.format("流程无需同步OA, status-%s, workflowId—%s, taskId-%s, requestId-%s"
                            , log.getStatus(), d.getWorkflowId(), d.getTaskId(), log.getRequestId()));
                    continue;
                }
                //未同步但是已经有记录的任务，执行下面的pushOaFlow进行同步。不再创建记录
            } else {
                //未有记录，创建记录
                TOaFlowSyncLog log = TOaFlowSyncLog.builder()
                        .workflowId(d.getWorkflowId())
                        .processDefKey(d.getProcessDefKey())
                        .processId(d.getProcessId())
                        .taskDefKey(d.getTaskDefKey())
                        .taskId(d.getTaskId())
                        .formData(JSONUtil.toJsonStr(d.getFormData()))
                        .applicant(d.getApplicant())
                        .approved(d.getApproved())
                        .status(OaFlowSyncLogStatusEnum.UNSYNC.getCode())
                        .build();
                syncLogRepo.save(log);
            }
            pushOaFlow(d.getWorkflowId(), d.getTaskId());
        }
        //getDataList不为空的情况下，剩下的是状态为SYNCED与UNSYNC，已经通过待办步骤同步过，但是不存在当前platform待办列表，说明已经结束了，全部修改为已结束
        logMap.forEach((taskId, lgMap) -> {
            if (!CollectionUtils.isEmpty(lgMap)) {
                lgMap.forEach((wid, l) -> {
                    l.setStatus(OaFlowSyncLogStatusEnum.END_GATEWAY.getCode());
                    syncLogRepo.save(l);
                    addFlowService.endOaFlow(l.getWorkflowId(), l.getTaskId());
                });
            }
        });
    }

    public boolean pushOaFlow(String workflowId, String taskId) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        String key = RedisKey.getOaflowSyncKey(workflowId, taskId);
        //先抢锁
        if (!ibltRedis.lock(key)) {
            logger.info("syncFlow2Oa error：redis抢锁失败， key-" + key);
            return true;
        }
        try {
            TOaFlowSyncLog log = syncLogRepo.findByWorkflowIdAndTaskId(workflowId, taskId);
            //创建oa流程
            logger.info("开始创建流程，FlowId:" + workflowId + ",参数:" + log.getFormData() + ",时间:" + DateUtils.getCurrentDate(DateUtils.formatSecond));
            //转义字段列表
            List<TOaFlowMetaTable> metaList = metaFlowService.getFlowMetaDataList(workflowId);
            Map<String, Object> dataMap = JSONUtil.parseObj(log.getFormData());
            //组装创建流程请求报文
            FlowObject fo = addFlowService.operateOAMessage(workflowId, dataMap, metaList);
            logger.info("创建流程调用参数为：" + JSONObject.toJSON(fo).toString());
            //调oa创建接口
            Map<String, String> resultMap = addFlowService.addOaFlow(JSONObject.toJSON(fo).toString());
            Integer requestId = Integer.valueOf(resultMap.get("requestid").toString());
            if (requestId > 0) {
                logger.info("完成流程创建，FlowId:" + workflowId + ",流程requestid:" + requestId + ",时间:" + DateUtils.getCurrentDate(DateUtils.formatSecond));
                syncLogRepo.updateStatusAndRequestId(workflowId, log.getTaskId(), requestId, OaFlowSyncLogStatusEnum.SYNCED.getCode());
            } else {
                logger.error("流程创建失败，FlowId:" + workflowId + ",流程requestid:" + requestId + ",时间:" + DateUtils.getCurrentDate(DateUtils.formatSecond));
                syncLogRepo.updateStatusAndRequestId(workflowId, log.getTaskId(), requestId, OaFlowSyncLogStatusEnum.CANCELLED.getCode());
            }
        } catch (Exception e) {
            logger.error("衡泰指令同步创建oa流程异常", e.getMessage());
        } finally {
            ibltRedis.unlock(key);
        }
        return false;
    }

    /**
     * 检查是否有oa未同步至oaflow的流程，
     */
    public void checkSysnLog() {
        //找所有已同步但未结束的log
        List<TOaFlowSyncLog> logs = syncLogRepo.findAllByStatusIn(Arrays.asList(OaFlowSyncLogStatusEnum.SYNCED.getCode()));
        if (CollectionUtils.isEmpty(logs)) {
            logger.info("无syncLog需要检查");
            return;
        }
        List<Integer> requestIds = logs.stream()
                .filter(l -> {
                    //如果requestid为空，表示还未同步至oa
                    if (Objects.isNull(l.getRequestId())) {
                        l.setStatus(OaFlowSyncLogStatusEnum.UNSYNC.getCode());
                        syncLogRepo.save(l);
                        return false;
                    } else {
                        return true;
                    }
                })
                .map(TOaFlowSyncLog::getRequestId)
                .distinct()
                .collect(Collectors.toList());
        Map<Integer, String> r2TMap = logs.stream().collect(Collectors.toMap(TOaFlowSyncLog::getRequestId, TOaFlowSyncLog::getTaskId));
        StringBuffer sb = new StringBuffer();
        logger.info("需同步：" + requestIds);
        sb.append("已同步：");
        if (!CollectionUtils.isEmpty(requestIds)) {
            //找到oa侧流程
            List<WorkflowRequestbase> oaFlows = oaFlowRepo.findAllByRequestidIn(requestIds);
            if (!CollectionUtils.isEmpty(oaFlows)) {
                oaFlows.forEach(oaFlow -> {
                    //oa侧已结束，发mq告知platform
                    if (OaWorkFlowRequestTableStatusEnum.isEnd(oaFlow.getStatus())) {
                        String desp;
                        if (OaWorkFlowRequestTableStatusEnum.isReject(oaFlow.getStatus())) {
                            //oa侧驳回，其流程被退回至发起人，仍未结束，在此强制结束oa流程
                            desp = OaFlowSyncMessageDescriptionEnum.REJECT.getDesc();
                            addFlowService.endOaFlow(String.valueOf(oaFlow.getWorkflowid()), r2TMap.get(oaFlow.getRequestid()));
                        } else {
                            desp = OaFlowSyncMessageDescriptionEnum.PASS.getDesc();
                        }
                        //发mq告知platform
                        oaCallController.forwardOARequest(String.valueOf(oaFlow.getWorkflowid()), String.valueOf(oaFlow.getRequestid()), oaFlow.getRequestname(), desp);
                        sb.append(oaFlow.getRequestid());
                        sb.append(",");
                    }
                });
            }
        }
        logger.info(sb.toString());
    }
}
